// my_predictor.h - Daan Raman
// Two-level adaptive branch predictor 

// We map the combination of history bits and index bits to index the counters array
#include <iostream>
#include <string>
#include <cstdlib>
#include <stdio.h>
#include <stdlib.h>
#include <sstream>
#include <cstdlib>

using namespace std;

class my_update : public branch_update {
public:
	unsigned int address;
};

class my_predictor : public branch_predictor {
public:
#define HISTORY_LENGTH	12
#define INDEX_BITS (26 - HISTORY_LENGTH)
	
	my_update u;
	branch_info bi;
	unsigned int history;
	unsigned int counters[1 << 29];
	unsigned int cIndex;
	//unsigned char
		
	//Global hashing index
	int gIndex;
	
	my_predictor (void) : history(0) { 
		//Empty
	}
	
	branch_update *predict (branch_info & b) {
		bi = b;
		if (b.br_flags & BR_CONDITIONAL) {
			//Save jump address
			u.address = b.address;
			
			//Get index for address part 
			cIndex = u.address & ((1<< (INDEX_BITS)) -1);
			gIndex = history | (cIndex << HISTORY_LENGTH);
			
			if(counters[gIndex] >= 2){
				u.direction_prediction(true);
			}else{
				u.direction_prediction(false);
			}
		} else {
			u.direction_prediction (true);
		}
		u.target_prediction (0);
		return &u;
	}
	
	void update (branch_update *u, bool taken, unsigned int target) {
		if (bi.br_flags & BR_CONDITIONAL) {
			
			//Get index for address part 
			cIndex = ((my_update*)u)->address & ((1<< (INDEX_BITS)) -1);
			gIndex = history | (cIndex << HISTORY_LENGTH);

			//printf("GETAL %d\n" , gIndex);
			
			if (taken) {
				if(counters[gIndex] < 3){
					counters[gIndex]++;
				}
			} else {
				if(counters[gIndex] >0){
					counters[gIndex]--;
				}
			}
			
			//Update history table with latest info on jump
			//Important to change history at the END of the update call, since gIndex uses the history state to index the counters array.
			history <<= 1;
			history |= taken;
			//printf("\n\n*****\n\n");
			history &= (1 << HISTORY_LENGTH) -1;
			
			
		}
	}
	
	void bin (int num) 
	{ 
		while (num != 0) 
		{ 
			printf ("%d",num % 2); 
			num = num /2; 
		} 
		
		printf("\n");
	}
	
};
